/***********************************************************************************************************
 *
 * <code>X86Translator</code> encode the series of <code>VInst</code> into the x86 instructions by invoking
 * the functions in X86.h
 *
 **********************************************************************************************************/
// X86Translator.h
// Defines the class X86Translator
#ifndef X86TRANSLATOR_H
#define X86TRANSLATOR_H

#include "crossbit/common.h"
#include "crossbit/Translator.h"
#include "crossbit/RegAllocator.h"
#include <map>
#include <set>
#include <deque>
#include <vector>

namespace crossbit {

    class TBlock;
    class VInst;

    class X86Translator : public Translator {
        public:
            const static int CACHE_SIZE = 1024;

            X86Translator(ProcessorState *state, RegAllocator *alloc);
            ~X86Translator();

            // maybe don't need this function now
            // TBlock * headBlock();

            void setRegAllocator(RegAllocator * allocator)
            {
                reg_allocator = allocator;
            }

            void set_hashtable_dispatch( XTMemAddr addr ); 
            void set_switch_point( XTMemAddr point);
            void set_sigret_point( XTMemAddr point);
            void encode(VBlock *vb, TBlock *tb);

#if 0
            void setContextReturn(void *ret) 
            {
                GLOBAL::return_address = (XTMemAddr)ret;
            }
#endif

            inline void setFirstTime()
            {
                this->first_time = true;
            }


            static struct RegisterFile {
                XTInt32 eax;
                XTInt32 ecx;
                XTInt32 edx;
                XTInt32 ebx;
                XTInt32 ebp;
                XTInt32 esp;
                XTInt32 esi;
                XTInt32 edi;
            } saved;

        private:
            void initTranslation();
            VBlock *vblock;
            TBlock *tblock;

            XTUint32 begin_exit;
            bool first_time;
            bool dirty_esp;
            XTInt32 saved_esp;

            // the Translator should hold a pointer of reg allocator
            RegAllocator* reg_allocator;


            // =========================================================
            // Wrapper functions of reg allocator, since allocator may 
            // use ESP, and the X86Translator should deal with it.
            XTRegNum X86RegAlloc(XTRegNum vreg, RegAccessMode mode);
            XTRegNum X86RegAllocForce(XTRegNum vreg, XTRegNum expect, RegAccessMode mode);
            XTRegNum X86RegAllocExpect(XTRegNum vreg, XTUint8 reg_mask, RegAccessMode mode);
            XTRegNum X86RegAllocForceExcept(XTRegNum vreg, XTRegNum except, RegAccessMode mode);
            void X86RegAllocReserve(XTRegNum treg);
            void X86RegAllocRelease(XTRegNum treg);

#if 0
            // =========================================================
            //	    Register Allocation Related

            // void usageCount();
            // void refCount(xtRegNum reg);

            enum RegAccessMode 
            {
                USE = 0, 
                DEF
            };

            XTRegNum regAlloc(XTRegNum vreg, RegAccessMode mode);
            XTRegNum regAllocForce(XTRegNum vreg, XTRegNum expect, RegAccessMode mode);
            XTRegNum regAllocForceExcept(XTRegNum vreg, XTRegNum except, RegAccessMode mode);

            void regAllocReserve(XTRegNum reg);
            void regAllocRelease(XTRegNum reg);

            XTRegNum regSpillOut(XTRegNum);
            void regSpillIn(XTRegNum vreg, XTRegNum treg);

            // spill memory pool
            XTInt32 * spill_pool;
            XTInt32 spill_pool_size;

            void unmapIfNoNextUse(XTRegNum vreg);


            // virtual register status
            const static XTInt32 UNMAPPED	= 0x01;
            const static XTInt32 MAPPED		= 0x02;
            const static XTInt32 SPILLED	= 0x04;	// already mapped but spilled into memory

            // target register status
            const static XTInt32 FREE		= 0x01;
            const static XTInt32 ALLOCATED	= 0x02;
            const static XTInt32 RESERVED	= 0x04;

            struct RegUsage {
                XTInt32 status;
                XTRegNum map;	// mapped target register number
                XTInt32 inst;	// belong to which instruction
            };

            typedef std::map<XTRegNum, struct RegUsage> RegUsageTable;
            RegUsageTable vreg_usg_tbl,	// virtual register usage table
                          treg_usg_tbl;	// target/physical register usage table

            typedef std::map<XTRegNum, XTInt32>   NextUse;
            typedef std::deque<NextUse>	    NextUseTable;
            NextUseTable next_use_tbl;

            void collectNextUseInfo();
            void printNextUseInfo();

            class NextUseLess {
                public:
                    bool operator()(const NextUse::value_type &lht,
                            const NextUse::value_type &rht)
                    {
                        return lht.second < rht.second;
                    }
            };

            std::set<XTRegNum> free_regs;		// free registers for allocation
            std::vector<XTRegNum> to_release_regs;	// registers will be released as soon as the instruction 
            // is finished execution
            // void printUsageTable(const UsageTable &info)
#endif

            // =========================================================
            //	    Exits Related

            void setBlockStub();	//for trace linking, added by zhengjuyu

            void endOfBlock();
            inline XTInt32 addTBlockExit(XTInt32 why, XTMemAddr next, XTInt32 exitOffset, VInst *inst);
            void encodeExits();


            // =========================================================
            //	    Encoding Related

            XTMemAddr inst_buf;	// instruction buffer
            XTMemAddr p_inst_buf;	// pointer to instruction buffer
            VInst *inst;	// current to-be-encoded virtual instruction

            void encodeGET();
            void encodePUT();
            void encodeLD();
            void encodeST();
            void encodeLI();
            void encodeLIRETURN();
            void encodeRETURN();
            void encodeMOV();
            void encodeSEXT();
            void encodeZEXT();
            void encodeADD();
            void encodeADDI();
            void encodeSUB();
            void encodeAND();
            void encodeXOR();
            void encodeOR();
            void encodeNOT();
            void encodeMUL();
            void encodeMULU();
            void encodeDIV();
            void encodeDIVU();
            void encodeSLL();
            void encodeSLLV();
            void encodeSRL();
            void encodeSRLV();
            void encodeSRA();
            void encodeSRAV();
            void encodeCMP();
            void encodeJMP();
            void encodeBRANCH();
            void encodeSYSCALL();
            void encodeSBREAK();

            void encodeFPUTS();
            void encodeFGETS();
            void encodeFPUTD();
            void encodeFGETD();

            void encodeFLD();
            void encodeFST();
            void encodeFMOV();

            void encodeFADDS();
            void encodeFADDD();
            void encodeFSUBS();
            void encodeFSUBD();
            void encodeFMULS();
            void encodeFMULD();
            void encodeFDIVS();
            void encodeFDIVD();
            void encodeFNEGS();
            void encodeFNEGD();
            void encodeFABSS();
            void encodeFABSD();
            void encodeFSQRTS();
            void encodeFSQRTD();
            void encodeFCMPS();
            void encodeFCMPD();

            void encodeFCONV();
            void encodeFINCSTP();
            void encodeCALL();

            XTInt32 *allocFpWord(XTInt32);
            XTInt64 *allocFpDword(XTInt64);
            
            // the temp simulated stack used in <code>encodeCALL</code>
            XTInt32 call_stack[CACHE_SIZE];  // 1k words
            XTInt32 fp_word_cache[CACHE_SIZE];
            XTInt64 fp_dword_cache[CACHE_SIZE];
            XTInt32 fp_word_count;
            XTInt32 fp_dword_count;
    };
}

#endif
